﻿using System;
using System.Collections.Generic;
using System.Reflection;

namespace Allegro.Interception
{
    //public static class InterceptingProxyFactory<I, T> where T : I, new()
    //{
    //    static public I CreateProxy(T realObject)
    //    {
    //        I o = default(T);

    //        return o;
    //    }
    //}
    ///<summary>
    ///</summary>
    ///<typeparam name="T"></typeparam>
    public static class ClassFactory<T> where T: new()
    {
        ///<summary>
        ///</summary>
        ///<returns></returns>
        static public T New()
        {
            T newObject = new T();
            return newObject;
        }
    }
    ///<summary>
    ///</summary>
    ///<typeparam name="I"></typeparam>
    public static class InterceptingProxyFactory<I>
    {
        private static Type _proxyType;

        ///<summary>
        ///</summary>
        ///<param name="realObject"></param>
        ///<returns></returns>
        ///<exception cref="ApplicationException"></exception>
        static public I CreateProxy(I realObject)
        {
            I o = default(I);
            if (_proxyType == null)
            {
                // Generate proxy type
            }
            if (_proxyType != null)
            {
                ConstructorInfo constructorInfo = _proxyType.GetConstructor(new Type[] {typeof (I)});
                if (constructorInfo != null)
                {
                    o = (I) constructorInfo.Invoke(new object[] {realObject});
                }
                else
                {
                    throw new ApplicationException(string.Format("Unable to instantiate proxy for interface type {0}",
                                                                 typeof (I).Name));
                }
            }
            return o;
        }
    }
    ///<summary>
    ///</summary>
    public interface I1
    {
        ///<summary>
        ///</summary>
        string Name { get; set; }
        ///<summary>
        ///</summary>
        I2 InnerInterface { get; set; }
    }
    ///<summary>
    ///</summary>
    public interface I2
    {
        ///<summary>
        ///</summary>
        string Name { get; set; }
    }

    //public abstract class InterceptingProxy
    //{
    //    internal abstract object Identity { get; }

    //    bool PreInvoke(string name, object[] args)
    //    {
    //        bool shouldInvoke = true;
    //        return shouldInvoke;
    //    }

    //}
    ///<summary>
    ///</summary>
    public abstract class InterceptingProxy // : InterceptingProxy
    {
        private object _realObject;
        private Type _type;

        static InterceptingProxy()
        {

        }
        /// <summary>
        /// 
        /// </summary>
        protected object RealObject
        {
            get { return _realObject; }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="realObject"></param>
        protected InterceptingProxy(object realObject)
        {
            _realObject = realObject;
            _type = realObject.GetType();
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="obj"></param>
        /// <returns></returns>
        public override bool Equals(object obj)
        {
            if (_realObject == null)
                return false;
            if (this.Equals(obj) || _realObject.Equals(obj))
                return true;
            InterceptingProxy proxy = obj as InterceptingProxy;
            if (proxy != null)
            {
                return (_realObject != null && proxy._realObject != null);
            }
            else
            {
                return false;
            }
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="currentMethod"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        protected bool PreInvoke(MethodBase currentMethod, object[] args)
        {
            bool shouldInvoke = true;
            return shouldInvoke;
        }
    }
    ///<summary>
    ///</summary>
    public class ProxyI1 : InterceptingProxy, I1
    {
        private I1 _o;
        private I2 _i2;

        ///<summary>
        ///</summary>
        ///<param name="realObject"></param>
        public ProxyI1(I1 realObject)
            : base(realObject)
        {
            _o = realObject;
            if (realObject.InnerInterface != null)
                _i2 = InterceptingProxyFactory<I2>.CreateProxy(realObject.InnerInterface);
        }
        /// <summary>
        /// 
        /// </summary>
        public string Name
        {
            get { return _o.Name; }
            set { _o.Name = value; }
        }
        /// <summary>
        /// 
        /// </summary>
        public I2 InnerInterface
        {
            get { return _i2; }
            set
            {
                if (value is InterceptingProxy)
                    _i2 = value;
                else
                {
                    _i2 = InterceptingProxyFactory<I2>.CreateProxy(_o.InnerInterface);
                }
            }
        }
    }
    ///<summary>
    ///</summary>
    public class ProxyI2 : InterceptingProxy, I2
    {
        private I2 _o;
        ///<summary>
        ///</summary>
        ///<param name="realObject"></param>
        public ProxyI2(I2 realObject)
            : base(realObject)
        {
            _o = realObject;
        }
        /// <summary>
        /// 
        /// </summary>
        public string Name
        {
            get { return _o.Name; }
            set { _o.Name = value; }
        }

    }
}
